package com.kexio.dynamic.datasource.aspect;

import com.kexio.dynamic.datasource.annotation.DS;
import com.kexio.dynamic.datasource.annotation.Master;
import com.kexio.dynamic.datasource.annotation.Slave;
import com.kexio.dynamic.datasource.service.DataSourceSwitchService;
import com.kexio.dynamic.datasource.service.TenantDataSourceRouter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * 数据源切换AOP切面
 * 
 * 拦截标注了@DS、@Master、@Slave注解的方法，
 * 自动切换到对应的数据源
 * 
 * @author Kexio
 * @since 1.0.0
 */
@Aspect
@Component
@Order(1) // 确保在事务切面之前执行
public class DataSourceAspect {

    private static final Logger log = LoggerFactory.getLogger(DataSourceAspect.class);

    private final DataSourceSwitchService dataSourceSwitchService;
    private final TenantDataSourceRouter tenantDataSourceRouter;

    public DataSourceAspect(DataSourceSwitchService dataSourceSwitchService,
                           TenantDataSourceRouter tenantDataSourceRouter) {
        this.dataSourceSwitchService = dataSourceSwitchService;
        this.tenantDataSourceRouter = tenantDataSourceRouter;
    }

    /**
     * @DS注解切点
     */
    @Pointcut("@within(com.kexio.dynamic.datasource.annotation.DS) || " +
              "@annotation(com.kexio.dynamic.datasource.annotation.DS)")
    public void dsPointcut() {
    }

    /**
     * @Master注解切点
     */
    @Pointcut("@within(com.kexio.dynamic.datasource.annotation.Master) || " +
              "@annotation(com.kexio.dynamic.datasource.annotation.Master)")
    public void masterPointcut() {
    }

    /**
     * @Slave注解切点
     */
    @Pointcut("@within(com.kexio.dynamic.datasource.annotation.Slave) || " +
              "@annotation(com.kexio.dynamic.datasource.annotation.Slave)")
    public void slavePointcut() {
    }

    /**
     * 数据源切换环绕通知
     */
    @Around("dsPointcut() || masterPointcut() || slavePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = point.getTarget().getClass();

        // 确定数据源
        String targetDataSource = determineDataSource(method, targetClass);
        
        // 保存当前数据源
        String originalDataSource = dataSourceSwitchService.getCurrentDataSource();
        
        try {
            // 切换数据源
            if (StringUtils.hasText(targetDataSource)) {
                switchDataSource(targetDataSource);
                log.debug("切换数据源: {} -> {}, 方法: {}.{}", 
                        originalDataSource, targetDataSource, 
                        targetClass.getSimpleName(), method.getName());
            }
            
            // 执行目标方法
            return point.proceed();
            
        } catch (Exception e) {
            log.error("数据源切换执行异常: 数据源={}, 方法={}.{}", 
                    targetDataSource, targetClass.getSimpleName(), method.getName(), e);
            
            // 根据失败策略处理异常
            handleFailure(method, targetClass, e);
            throw e;
            
        } finally {
            // 恢复原数据源
            if (StringUtils.hasText(originalDataSource)) {
                switchDataSource(originalDataSource);
                log.debug("恢复数据源: {} -> {}", targetDataSource, originalDataSource);
            } else {
                dataSourceSwitchService.clear();
            }
        }
    }

    /**
     * 确定目标数据源
     */
    private String determineDataSource(Method method, Class<?> targetClass) {
        // 优先级：方法注解 > 类注解
        
        // 1. 检查方法上的@DS注解
        DS ds = AnnotationUtils.findAnnotation(method, DS.class);
        if (ds != null) {
            return resolveDataSourceName(ds);
        }
        
        // 2. 检查方法上的@Master注解
        Master master = AnnotationUtils.findAnnotation(method, Master.class);
        if (master != null) {
            return "master";
        }
        
        // 3. 检查方法上的@Slave注解
        Slave slave = AnnotationUtils.findAnnotation(method, Slave.class);
        if (slave != null) {
            return selectSlaveDataSource(slave);
        }
        
        // 4. 检查类上的@DS注解
        ds = AnnotationUtils.findAnnotation(targetClass, DS.class);
        if (ds != null) {
            return resolveDataSourceName(ds);
        }
        
        // 5. 检查类上的@Master注解
        master = AnnotationUtils.findAnnotation(targetClass, Master.class);
        if (master != null) {
            return "master";
        }
        
        // 6. 检查类上的@Slave注解
        slave = AnnotationUtils.findAnnotation(targetClass, Slave.class);
        if (slave != null) {
            return selectSlaveDataSource(slave);
        }
        
        // 7. 使用租户路由器自动确定
        return tenantDataSourceRouter.determineCurrentDataSource();
    }

    /**
     * 解析数据源名称
     */
    private String resolveDataSourceName(DS ds) {
        String dataSourceName = ds.value();
        
        // 处理自动类型
        if (ds.type() == DS.DSType.AUTO) {
            if (ds.readOnly()) {
                return dataSourceSwitchService.selectSlave();
            }
            if (!StringUtils.hasText(dataSourceName)) {
                return tenantDataSourceRouter.determineCurrentDataSource();
            }
        }
        
        // 处理特定类型
        switch (ds.type()) {
            case MASTER:
                return "master";
            case SLAVE:
                return dataSourceSwitchService.selectSlave();
            case TENANT:
                return tenantDataSourceRouter.determineCurrentDataSource();
            case CUSTOM:
            default:
                break;
        }
        
        // 解析表达式
        if (StringUtils.hasText(dataSourceName)) {
            return dataSourceSwitchService.resolveDataSourceExpression(dataSourceName);
        }
        
        return null;
    }

    /**
     * 选择从库数据源
     */
    private String selectSlaveDataSource(Slave slave) {
        if (slave.loadBalance()) {
            String strategy = slave.strategy().name().toLowerCase();
            return dataSourceSwitchService.selectSlave(strategy);
        }
        return dataSourceSwitchService.selectSlave();
    }

    /**
     * 切换数据源
     */
    private void switchDataSource(String dataSourceName) {
        try {
            dataSourceSwitchService.switchTo(dataSourceName);
        } catch (Exception e) {
            log.warn("数据源切换失败: {}", dataSourceName, e);
            // 根据配置决定是否抛出异常或使用默认数据源
        }
    }

    /**
     * 处理失败情况
     */
    private void handleFailure(Method method, Class<?> targetClass, Exception e) {
        // 获取失败策略
        DS.FailureStrategy strategy = getFailureStrategy(method, targetClass);
        
        switch (strategy) {
            case THROW_EXCEPTION:
                // 异常已经在上层抛出
                break;
            case FALLBACK_TO_DEFAULT:
                log.warn("数据源操作失败，回退到默认数据源: 方法={}.{}", 
                        targetClass.getSimpleName(), method.getName());
                dataSourceSwitchService.reset();
                break;
            case IGNORE:
                log.debug("忽略数据源操作失败: 方法={}.{}", 
                        targetClass.getSimpleName(), method.getName());
                break;
        }
    }

    /**
     * 获取失败策略
     */
    private DS.FailureStrategy getFailureStrategy(Method method, Class<?> targetClass) {
        // 方法注解优先
        DS ds = AnnotationUtils.findAnnotation(method, DS.class);
        if (ds != null) {
            return ds.onFailure();
        }
        
        Master master = AnnotationUtils.findAnnotation(method, Master.class);
        if (master != null) {
            return master.onFailure();
        }
        
        Slave slave = AnnotationUtils.findAnnotation(method, Slave.class);
        if (slave != null) {
            return slave.onFailure();
        }
        
        // 类注解
        ds = AnnotationUtils.findAnnotation(targetClass, DS.class);
        if (ds != null) {
            return ds.onFailure();
        }
        
        master = AnnotationUtils.findAnnotation(targetClass, Master.class);
        if (master != null) {
            return master.onFailure();
        }
        
        slave = AnnotationUtils.findAnnotation(targetClass, Slave.class);
        if (slave != null) {
            return slave.onFailure();
        }
        
        return DS.FailureStrategy.FALLBACK_TO_DEFAULT;
    }
}
